Some instrument Plug-ins support Key switching functionality, which allows a user to switch between different layered sounds while playing notes, to achieve this the user has to pressed a specific key associated to this wanted layer before playing new notes.
A typical example is a sample-based-Player Plug-in with a violin sound which could include different layers or articulations: pizzicato, legato, tremolo... By pressing or by keeping pressed a specific key, for example C-1, before playing new notes, the Plug-in will choose to play pizzicato, by using D-1, it will play legato...
With VST Expression Map (feature of Cubase since 4.0), these Key switches could be used in score editor to associated a symbol (articulation) to a note and each time this note will be played the corresponding Key switch will be used (send to the Plug-in as noteOn event).
In order to help the creation of such Map, VST 3.5 defines a new interface Steinberg::Vst::IKeyswitchController. When a (instrument) Plug-in supports such interface, the host could get from the Plug-in the current set of used Key switches (megatrigg / articulation: Steinberg::Vst::KeyswitchInfo) for a given channel of a Event Bus and then automatically use them (like in Cubase 6) to create VST Expression Map.
Here the Step by Step example.
We want a Plug-in with 1 event bus and mono-timbral (1 channel) which supports 2 Key switches:
1. The instrument Plug-in should have one input event bus (could be more).
//------------------------------------------------------------------------ tresult PLUGIN_API MyExampleProcessor::initialize (FUnknown* context) { //---always initialize the parent------- tresult result = AudioEffect::initialize (context); if (result == kResultTrue) { // we want a Stereo Output addAudioOutput (STR16 ("Stereo Output"), SpeakerArr::kStereo); // create Event In bus (1 bus with only 1 channel) addEventInput (STR16 ("Event Input"), 1); } return result; } //------------------------------------------------------------------------
2. The controller should have the interface Steinberg::Vst::IKeyswitchController, here in the class declaration:
//----------------------------------------------------------------------------- class MyExampleController: public EditController, public IKeyswitchController { public: ... //---from IKeyswitchController virtual int32 PLUGIN_API getKeyswitchCount (int32 busIndex, int16 channel); virtual tresult PLUGIN_API getKeyswitchInfo (int32 busIndex, int16 channel, int32 keySwitchIndex, KeyswitchInfo& info); ... OBJ_METHODS (MyExampleController, EditController) DEFINE_INTERFACES DEF_INTERFACE (IKeyswitchController) END_DEFINE_INTERFACES (EditController) REFCOUNT_METHODS(EditController) ... }; //------------------------------------------------------------------------
3. Now we have to implement the interface Steinberg::Vst::IKeyswitchController (only 2 functions), in our example Steinberg::Vst::IKeyswitchController::getKeyswitchCount should return 2 (2 keyswitches):
//------------------------------------------------------------------------ int32 MyExampleController::getKeyswitchCount (int32 busIndex, int16 channel) { // we accept only the first bus and 1 channel if (busIndex == 0 && channel == 0) return 2; return 0; } //------------------------------------------------------------------------
4. Then we have to implement Steinberg::Vst::IKeyswitchController::getKeyswitchInfo which allows to inform the host about what the Plug-in supports:
//------------------------------------------------------------------------ tresult PLUGIN_API MyExampleController::getKeyswitchInfo (int32 busIndex, int16 channel, int32 keySwitchIndex, KeyswitchInfo& info) { // we accept only the first bus and 1 channel and only 2 keyswitches if (busIndex == 0 && channel == 0 && (keySwitchIndex == 0 || keySwitchIndex == 1) { memset (&info, 0, sizeof (KeyswitchInfo)); info.typeId = kKeyRangeTypeID; // in this case we want that Keyswitch should be maintained pressed for playĆng // we could use keyRemapped to make easier the use of the keyswitch (near the available instrument key range) // take care that there are no overlap between keyswitches and real key (playing sound) if (keySwitchIndex == 0) { USTRING ("Accentuation").copyTo (info.title, 128); USTRING ("Acc").copyTo (info.shortTitle, 128); // if the user keeps pressed C-1 or C#-1 or C-0 then the Accentuation sound should be played info.keyswitchMin = 12; // C-1 info.keyswitchMax = 13; // C#-1 info.keyRemapped = 24; // C-0 } else { USTRING ("Softly").copyTo (info.title, 128); USTRING ("Soft").copyTo (info.shortTitle, 128); // if the user keeps pressed D-1 or D#-1 or D-0 then the Softly sound should be played info.keyswitchMin = 14; // D-1 info.keyswitchMax = 15; // D#-1 info.keyRemapped = 26; // D-0 } info.unitID = -1; // not used info.flags = 0; // not used return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------
5. Last step, in the processor component we have to adapt the process call to switch to the wanted articulation:
//------------------------------------------------------------------------ tresult MyExampleProcessor::process (ProcessData& data) { .... // get the input event queue IEventList* inputEvents = data.inputEvents; if (inputEvents) { Event e; int32 numEvents = inputEvents->getEventCount (); // for each events check it.. for (int32 i = 0; i < numEvents; i++) { if (inputEvents->getEvent (i, e) == kResultTrue) { switch (e.type) { //----------------------- case Event::kNoteOnEvent: { // here a note On // check if this is a Keyswitch switch (e.noteOn.pitch) { // Accentuation Keyswitch case 12: case 13: case 24: currentLayer = kAccentuationLayer; break; // Softly Keyswitch case 14: case 15: case 26: currentLayer = kSoftlyLayer; break; default: // play the note in the currentLayer ... } } break; //----------------------- case Event::kNoteOffEvent: { // check if keyswitch is released switch (e.noteOff.pitch) { // Accentuation Keyswitch case 12: case 13: case 24: case 14: case 15: case 26: currentLayer = kDefaultLayer; break; default: // released note... ... } } break; .... } } } } ... } //------------------------------------------------------------------------
That is it!
Back to Contents